/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 simulated_combat.h

	$Header: /heroes4/simulated_combat.h $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "simulated_combat.h"

#include "adventure_ai.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "ai_value.h"
#include "cached_grail_data_source.h"
#include "can_cast.h"
#include "combat_action_message.h"
#include "combat_creature.h"
#include "combat_cursor_mode.h"
#include "combat_result.h"
#include "combat_start_position.h"
#include "creature.h"
#include "creature_ability.h"
#include "creature_array.h"
#include "creature_traits.h"
#include "creature_type.h"
#include "enum_operations.h"
#include "hero.h"
#include "map_point.h"
#include "null_grail_data_source.h"
#include "player.h"
#include "random.h"
#include "report_combat_results.h"
#include "simulated_combat_creature.h"
#include "spell_properties.h"
#include "town.h"

// -----------------------------------------------------
// creature data for a simulated combat
// -----------------------------------------------------
t_simulated_combat_creature::t_simulated_combat_creature( t_creature_stack* creature, 
														  int slot, bool defender,
														  double effectiveness,
														  t_abstract_grail_data_source const& grail_data,
														  t_player const* owner)
						   : t_abstract_combat_creature( creature, slot, defender, owner ),
						     m_grail_data( grail_data )
{
	m_effectiveness = effectiveness;
	m_position = 0;
	m_wounds = creature->get_wounds();
	m_offense = 1.0;
	m_defense = 1.0;
	m_spell_offense = 1.0;
	m_spell_defense = 1.0;
	m_lifespan = -1;
	m_first_casualty_time = -1;
}

// -----------------------------------------------------
// creature data for a simulated combat
// copy information from a "real" combat creature for look-ahead simuluations
// -----------------------------------------------------
t_simulated_combat_creature::t_simulated_combat_creature( t_combat_creature const& creature )
                           : t_abstract_combat_creature( creature ),
							 m_grail_data( creature.get_grail_data() )
{
	m_effectiveness = 1.0;
	m_position = creature.get_cell_position().row;
	m_offense = 1.0;
	m_defense = 1.0;
	m_spell_offense = 1.0;
	m_spell_defense = 1.0;
	m_lifespan = -1;

	t_spell_list const&			 spells = creature.get_active_spells();
	t_spell_list::const_iterator index = spells.begin();
	t_spell_list::const_iterator end = spells.end();

	for (; index != end; ++index)
	{
		set_spell( *index );
	}
}

t_abstract_grail_data_source const& t_simulated_combat_creature::get_grail_data() const
{
	return m_grail_data;
}

// -----------------------------------------------------
// creature data for a simulated combat
// -----------------------------------------------------
void t_simulated_combat_creature::add_wounds( int arg, int turn )
{
	if (m_number == 0)
		return;

	int deaths;
	int hit_points = get_hit_points();

	m_wounds += arg;
	deaths = m_wounds / hit_points;
	m_wounds -= deaths * hit_points;
	m_number -= deaths;
	if (deaths > 0 && m_first_casualty_time < 0)
		m_first_casualty_time = turn;
	if (m_number <= 0)
	{
		m_number = 0;
		m_wounds = 0;
		m_lifespan = turn;
	}
}

// -----------------------------------------------------
// creature data for a simulated combat
// -----------------------------------------------------
bool t_simulated_combat_creature::can_retaliate( t_simulated_combat_creature const& attacker ) const
{
	if (m_has_retaliated)
		return false;
	if (get_position() != attacker.get_position())
	{
		if (!can_shoot())
			return false;
	}
	else
	{
		if (attacker.has_ability( k_ability_no_retaliation ))
			return false;
		if (attacker.has_ability( k_ability_long_weapon )
			&& !has_ability( k_ability_long_weapon ))
			return false;
	}
	return true;
}

// -----------------------------------------------------
// creature data for a simulated combat
// -----------------------------------------------------
bool t_simulated_combat_creature::can_shoot() const
{
	return m_shots > 0 && has_ability( k_ability_ranged );
}

// -----------------------------------------------------
// creature data for a simulated combat
// -----------------------------------------------------
void t_simulated_combat_creature::do_attack( t_simulated_combat_creature&	defender,
											 bool							attacker_has_first_strike,
											 bool							defender_has_first_strike,
											 int							attacks,
											 int							turn )
{
	int attacker_damage;
	int defender_damage;

	if (attacker_has_first_strike && defender_has_first_strike)
	{
		attacker_has_first_strike = false;
		defender_has_first_strike = false;
	}
	if (!defender.can_retaliate( *this ))
	{
		defender_damage = get_damage( defender );
		defender.add_wounds( defender_damage, turn );
	} 
	else
	{
		if (attacker_has_first_strike)
		{
			defender_damage = get_damage( defender );
			defender.add_wounds( defender_damage, turn );
			if (defender.get_number() > 0)
			{
				attacker_damage = defender.get_damage( *this );
				add_wounds( attacker_damage, turn );
			}
		} 
		else if (defender_has_first_strike)
		{
			attacker_damage = defender.get_damage( *this );
			add_wounds( attacker_damage, turn );
			if (get_number() > 0)
			{
				defender_damage = get_damage( defender );
				defender.add_wounds( defender_damage, turn );
			}
		} 
		else 
		{
			defender_damage = get_damage( defender );
			attacker_damage = defender.get_damage( *this );
			add_wounds( attacker_damage, turn );
			defender.add_wounds( defender_damage, turn );
		}
		if (!defender.has_ability( k_ability_unlimited_retaliation ))
			defender.set_retaliated( true );
	}
	while (--attacks > 0 && get_number() > 0 && defender.get_number() > 0)
	{
		defender_damage = get_damage( defender );
		defender.add_wounds( defender_damage, turn );
	}
}

// -----------------------------------------------------
// creature data for a simulated combat
// -----------------------------------------------------
void t_simulated_combat_creature::do_melee( t_simulated_combat_creature& defender, int turn )
{
	bool attacker_has_first_strike = has_ability( k_ability_first_strike )
								  && !defender.has_ability( k_ability_first_strike_immunity );
	bool defender_has_first_strike = defender.has_ability( k_ability_first_strike )
								  && !has_ability( k_ability_first_strike_immunity );
	int  attacks = 1;

	if (has_ability( k_ability_strikes_twice ))
		++attacks;
	do_attack( defender, attacker_has_first_strike, defender_has_first_strike, attacks, turn );
}

// -----------------------------------------------------
// creature data for a simulated combat
// -----------------------------------------------------
void t_simulated_combat_creature::do_ranged_attack( t_simulated_combat_creature& defender, int turn )
{
	bool attacker_has_first_strike = has_ability( k_ability_ranged_first_strike );
	bool defender_has_first_strike = defender.has_ability( k_ability_ranged_first_strike );
	int  attacks = 1;

	if (has_ability( k_ability_shoots_twice ))
		++attacks;
	if (attacks > m_shots)
		attacks = m_shots;
	do_attack( defender, attacker_has_first_strike, defender_has_first_strike, attacks, turn );
	m_shots -= attacks;
}

// -----------------------------------------------------
// creature data for a simulated combat
// -----------------------------------------------------
int t_simulated_combat_creature::get_damage( t_simulated_combat_creature const& defender ) const
{
	int	    distance = abs( get_position() - defender.get_position() );
	bool	ranged = distance > 0;
	double  damage = get_damage_low() + get_damage_high();
	double  defense = m_defense;

	damage = damage * get_number() * m_effectiveness * m_offense;
	if (ranged)
		defense = defense * get_range_effect( distance, false );
	damage = damage * 0.5 / defense;
	damage = damage * get_damage_modifier( defender, ranged );
	if (damage < 1.0)
		damage = 1.0;
	return damage;
}

// -----------------------------------------------------
// creature data for a simulated combat
// -----------------------------------------------------
void t_simulated_combat_creature::process_new_turn()
{
	set_moved( false );
	set_retaliated( false );
}

// ------------------------------------------------------------------------------
// heal damage
// ------------------------------------------------------------------------------
void t_simulated_combat_creature::heal( int amount )
{
	m_wounds -= amount;
	if (m_wounds < 0)
		m_wounds = 0;
}

// ------------------------------------------------------------------------------
// heal & resurrect damage
// ------------------------------------------------------------------------------
void t_simulated_combat_creature::resurrect( int amount )
{
	m_wounds -= amount;
	if (m_wounds < 0)
	{
		int revived;
		int original_number = get_original_number();
		int hit_points = get_hit_points();

		revived = (hit_points - m_wounds - 1) / hit_points;
		m_number += revived;
		if (m_number > original_number)
		{
			m_number = original_number;
			m_wounds = 0;
		}
		else
		{
			m_wounds += hit_points * revived;
		}
	}
}

// ------------------------------------------------------------------------------
// cast a given spell on a creature
// ------------------------------------------------------------------------------
void t_simulated_combat_creature::set_enchantment( t_spell spell, int numerator, int denominator )
{
	if (affects_magic( spell ))
	{
		if (affects_offense( spell ))
			m_spell_offense = m_spell_offense * numerator / denominator;
		if (affects_defense( spell ))
			m_spell_defense = m_spell_defense * numerator / denominator;
	}
	else
	{
		if (affects_offense( spell ))
			m_offense = m_offense * numerator / denominator;
		if (affects_defense( spell ))
			m_defense = m_defense * numerator / denominator;
	}
}

// ------------------------------------------------------------------------------
// get adjusted spell damage
// ------------------------------------------------------------------------------
static int get_spell_damage( t_simulated_combat_creature const& attacker,
							 t_spell							spell,
							 t_simulated_combat_creature const& defender,
							 int								power )
{
	int result =  power * attacker.get_spell_offense()
				        * (100 - defender.get_magic_resistance( spell )) 
					    / (100.0 * defender.get_spell_defense());
	int hits = defender.get_total_hits();

	if (result > hits)
		result = hits;
	return result;
}

// ------------------------------------------------------------------------------
// get adjusted spell damage
// ------------------------------------------------------------------------------
inline int get_spell_damage( t_simulated_combat_creature const& attacker,
							 t_spell							spell,
							 t_simulated_combat_creature const& defender )
{
	return get_spell_damage( attacker, spell, defender, attacker.get_spell_power( spell ));
}


// ------------------------------------------------------------------------------
// Don't actually need message in simulated combat
// ------------------------------------------------------------------------------
void t_simulated_combat_creature::set_spell( t_spell spell, int power, t_spell_source source )
{
	set_spell( spell, t_combat_action_message(), power, source );
}

// ------------------------------------------------------------------------------
// cast a given spell on a creature
// ------------------------------------------------------------------------------
void t_simulated_combat_creature::set_spell( t_spell spell, t_combat_action_message const & message, int power, t_spell_source )
{
	t_spell_effect& effect = m_spells[spell];

	if (effect.active)
		return;

	double			adjusted_power;
	t_ai_spell_type spell_type;
	
	spell_type = get_ai_spell_type( spell );
	// for blessings and curses, the power parameter includes the bonus (in percentage points)
	// to the effect of the curse / blessing.
	if (spell_type == k_ai_spell_type_blessing || spell_type == k_ai_spell_type_curse)
	{
		// get the basic power based on an estimate of the spell's effect
		adjusted_power = get_ai_spell_value( spell );
		if (!affects_magic( spell ))
		{
			adjusted_power = adjusted_power * (power + 100) / 100.0;
		}
	}
	else
	{   // other spells use raw damage / healing
		adjusted_power = power;
	}

	// reduce effect of hostile spells by magic resistance and magic defense
	if (!affects_magic( spell ))
	{
		if (spell_type == k_ai_spell_type_curse)
			adjusted_power = adjusted_power * (100 - get_magic_resistance( spell ))
			/ (100.0 * m_spell_defense);
	}
	switch (spell_type)
	{
		case k_ai_spell_type_blessing:
			set_enchantment( spell, adjusted_power + 100, 100 );
			break;

		case k_ai_spell_type_curse:
			set_enchantment( spell, 100, adjusted_power + 100 );
			break;

		case k_ai_spell_type_healing:
			heal( adjusted_power );
			return;

		case k_ai_spell_type_resurrection:
			resurrect( adjusted_power );
			return;
	}
	effect.power = adjusted_power;
	effect.active = true;
}

// ------------------------------------------------------------------------------
// cast a given spell on a creature
// ------------------------------------------------------------------------------
void t_simulated_combat_creature::cast_spell_on( t_spell spell, 
												 t_simulated_combat_creature& target )
{
	double			power;
	t_ai_spell_type spell_type;

	spell_type = get_ai_spell_type( spell );
	switch (spell_type)
	{
		case k_ai_spell_type_blessing:
		case k_ai_spell_type_curse:
			// fill power parameter with percentage bonus
			power = (m_spell_offense - 1.0) * 100.0;
			break;

		default:
			power = m_spell_offense * get_spell_power( spell );
			break;
	}

	target.set_spell( spell, power );
}

// -----------------------------------------------------------
// add simulated creatures
// -----------------------------------------------------------
static void add_creatures( t_creature_array&				   army, 
						   t_simulated_combat_creature_list&   list,
						   bool								   belongs_to_defender,
						   double							   effectiveness,
						   t_abstract_grail_data_source const& grail_data,
						   t_player const*					   owner )
{
	int							 i;
	t_simulated_combat_creature* creature;
	t_map_point_2d const*        start_position;

	start_position = k_combat_start_position[k_formation_loose][belongs_to_defender];

	for (i = 0; i < t_creature_array::k_size; ++i)
	{
		if ( army[i].get_number() == 0)
			continue;
		if ( army[i].is_dead() || army[i].get_wounds() >= army[i].get_hit_points())
			continue;

		creature = new t_simulated_combat_creature( &army[i], i, belongs_to_defender,
													effectiveness, grail_data, owner );
		creature->set_position( start_position[i].row );
		list.push_back( creature );
	}
}

// -----------------------------------------------------------
// class which depicts a simulate combat between two sides
// -----------------------------------------------------------
t_simulated_combat::t_simulated_combat( t_creature_array&			attacker,
										t_player*					attacking_player, 
										t_creature_array&			defenders,
										t_player*					defending_player,
										t_adventure_map&			adventure_map,
										t_adv_map_point const&		location,
										t_town*						town )
{
	if (attacking_player == 0)
		m_grail_data[0].reset( new t_null_grail_data_source );
	else
		m_grail_data[0].reset( new t_cached_grail_data_source( *attacking_player ) );
	if (defending_player != 0)
		m_grail_data[1].reset( new t_cached_grail_data_source( *defending_player ) );
	else if (town != 0)
		m_grail_data[1].reset( new t_cached_grail_data_source( *town ));
	else
		m_grail_data[1].reset( new t_null_grail_data_source );
	

	m_spellcasting_allowed[0] = true;
	m_spellcasting_allowed[1] = true;
	m_armies[0] = &attacker;
	m_armies[1] = &defenders;
	m_players[0] = attacking_player;
	m_players[1] = defending_player;
	add_creatures( attacker, m_creatures, false, random( 85, 115 ) / 100.0, *m_grail_data[0], attacking_player );
	add_creatures( defenders, m_creatures, true, random( 85, 115 ) / 100.0, *m_grail_data[1], defending_player );
	m_leaders[0] = &attacker.get_leader();
	m_leaders[1] = &defenders.get_leader();
	m_result = k_result_unknown;
	m_location = location;
	m_town = town;
	m_sea_battle = false;
	if (!location.on_bridge && adventure_map.is_valid(location) && adventure_map.get_tile(location).get_terrain() == k_terrain_water)
		m_sea_battle = true;
}

// -----------------------------------------------------------
// class which depicts a simulated combat between two sides
// -----------------------------------------------------------
t_simulated_combat::t_simulated_combat( t_creature_array**	armies,
										t_player**			players, 
										t_town*				town,
										bool				sea_battle,
										t_abstract_grail_data_source const& attacker_grail_data,
										t_abstract_grail_data_source const& defender_grail_data )
{
	int i;

	m_grail_data[0].reset( new t_cached_grail_data_source( attacker_grail_data ) );
	m_grail_data[1].reset( new t_cached_grail_data_source( defender_grail_data ) );
	m_spellcasting_allowed[0] = true;
	m_spellcasting_allowed[1] = true;
	for (i = 0; i < 2; ++i)
	{
		assert( m_armies[i] );
		m_armies[i] = armies[i];
		m_players[i] = players[i];
		m_leaders[i] = &armies[i]->get_leader();
	}
	m_result = k_result_unknown;
	m_location = m_location;
	m_town = town;
	m_sea_battle = sea_battle;
}

t_simulated_combat::~t_simulated_combat() 
{
}

void t_simulated_combat::add( t_simulated_combat_creature* creature )
{
	m_creatures.push_back( creature );
}

// ------------------------------------------------------------------------------
// cast a damage spell
// ------------------------------------------------------------------------------
void t_simulated_combat::cast_damage( t_simulated_combat_creature& caster,
									  t_spell					   spell,
									  t_simulated_combat_creature& target )
{
	int	attacks = get_ai_spell_value( spell ) - 1;

	target.add_wounds( get_spell_damage( caster, spell, target ), m_turns );
	if (attacks <= 0)
		return;

	t_simulated_combat_creature_list::iterator  index;
	t_simulated_combat_creature*				creature;

	for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
	{
		creature = *index;
		if (creature == &target)
			continue;
		if (creature->get_number() == 0)
			continue;
		if (creature->belongs_to_defender() == caster.belongs_to_defender())
			continue;
		if (!can_affect_target( caster, spell, *creature ))
			continue;
		creature->add_wounds( get_spell_damage( caster, spell, *creature ), m_turns );
		if (--attacks == 0)
			return;
	}
}


// ------------------------------------------------------------------------------
// cast a given spell
// ------------------------------------------------------------------------------

void t_simulated_combat::cast_spell( t_simulated_combat_creature& caster,
									 t_spell					  spell,
									 t_simulated_combat_creature* target,
									 t_simulated_combat_creature* client )
{
	int				cost = caster.get_spell_cost( spell );
	int				spell_points = caster.get_spell_points();
	t_ai_spell_type spell_type;

	spell_type = get_ai_spell_type( spell );
	caster.set_spell_points( spell_points - cost );
	// mass effect spells affect all creatures
	if (is_mass_effect( spell ))
	{
		t_simulated_combat_creature_list::iterator  index;

		for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
		{
			// check if target is legal
			target = *index;
			if (target->get_number() == 0)
				continue;
			if (!can_affect_target( caster, spell, *target ))
				continue;
			caster.cast_spell_on( spell, *target );
		}
		return;
	}
	switch (spell_type)
	{
		case k_ai_spell_type_summoning:
			cast_summoning( caster, spell );
			break;

		case k_ai_spell_type_sacrifice:
		{
			int hits = target->get_total_hits();

			target->add_wounds( hits, m_turns );
			client->resurrect( hits * 2 );
			break;
		}

		case k_ai_spell_type_damage:
			cast_damage( caster, spell, *target );
			break;

		case k_ai_spell_type_hand_of_death:
		{
			double power;

			power = caster.get_spell_offense() * caster.get_spell_power( spell )
					* target->get_hit_points();
			power = power * (100 - target->get_magic_resistance( spell )) 
					/ (100.0 * target->get_spell_defense());
			target->add_wounds( power, m_turns );
			break;
		}

		default:
			caster.cast_spell_on( spell, *target );
			break;
	}
}


// ------------------------------------------------------------------------------
// cast a summoning spell
// ------------------------------------------------------------------------------
void t_simulated_combat::cast_summoning( t_simulated_combat_creature& caster,
										 t_spell					  spell )
{
	t_creature_type				 creature_type = get_summoned_creature( spell );
	int							 number = caster.get_spell_power( spell );
	t_creature_stack*			 stack;
	t_simulated_combat_creature* target;

	if (creature_type == k_creature_none)
	{ // summoning spells without creatures have power expressed in hit points,
	  // not actual number of creatures
		creature_type = k_pikeman;
		number = number / get_traits( creature_type ).hit_points;
		if (number < 1)
			return;
	}

	bool defender = caster.belongs_to_defender();

	stack = new t_creature( creature_type, number );
	target = new t_simulated_combat_creature( stack, -1, defender, caster.get_effectiveness(),
											  caster.get_grail_data(), m_players[ defender ] );
	target->set_offense( m_tactics_offense_bonus[ defender ] );
	target->set_defense( m_tactics_defense_bonus[ defender ] );
	target->set_position( caster.get_position() );
	target->set_moved( true );
	m_creatures.push_back( target );
}

// -----------------------------------------------------------
// class which depicts a simulated combat between two sides
// simulate a single creature's actions
// -----------------------------------------------------------
bool t_simulated_combat::choose_action( t_simulated_combat_creature& attacker )
{
	bool									    belongs_to_defender;
	t_simulated_combat_creature_list::iterator  index;
	int											distance;
	int											closest;
	t_simulated_combat_creature*				target;
	t_simulated_combat_creature*				best_target = 0;
	int											position = attacker.get_position();

	// simplest possible decision making: choose the closest creature,
	// and between equally close creatures, choose the one that has not retaliated.
	belongs_to_defender = attacker.belongs_to_defender();
	for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
	{
		target = *index;
		if (target->belongs_to_defender() == belongs_to_defender)
			continue;
		if (target->get_number() == 0)
			continue;
		distance = abs( target->get_position() - position );
		if (best_target != 0)
		{
			if (closest < distance)
				continue;
			if (closest == distance &&  !best_target->can_retaliate( attacker ))
				continue;
		}
		closest = distance;
		best_target = target;
	}
	if (best_target == 0)
		return false;

	int								damage = attacker.get_damage( *best_target );
	t_spell							spell = k_spell_none;
	t_simulated_combat_creature*	spell_target = 0;
	t_simulated_combat_creature*	spell_client = 0;

	if (m_spellcasting_allowed[attacker.get_controller()]
		&& damage < choose_spell( attacker, spell, spell_target, spell_client ))
	{
		cast_spell( attacker, spell, spell_target, spell_client );
		attacker.set_moved( true );
		return true;
	}

	t_combat_cursor_mode action = k_combat_mode_ranged;

	// if we are at zero distance, we must melee
	if (closest == 0 || !attacker.can_shoot())
		action = k_combat_mode_melee;
	else
	{
		action = k_combat_mode_ranged;
		if (best_target->can_shoot())
		{
			int attacker_range_effect = attacker.get_range_effect( closest, false );
			int defender_range_effect = best_target->get_range_effect( closest, false );

			if (defender_range_effect < attacker_range_effect)
				action = k_combat_mode_melee;
		}
	}

	if (action == k_combat_mode_ranged)
		attacker.do_ranged_attack( *best_target, m_turns );
	else
	{
		if (closest > 0)
		{
			int movement = attacker.get_combat_movement() / 100;

			if (movement > closest)
				movement = closest;
			if (best_target->get_position() > position)
				attacker.set_position( position + movement );
			else
				attacker.set_position( position - movement );
			closest = abs( best_target->get_position() - attacker.get_position() );
		}
		if (closest == 0)
			attacker.do_melee( *best_target, m_turns );
	}
	attacker.set_moved( true );
	return true;
}

// ------------------------------------------------------------------------------
// choose target for a damage spell
// ------------------------------------------------------------------------------
int t_simulated_combat::choose_damage_target( t_simulated_combat_creature const& caster,
										      t_spell							  spell,
											  t_simulated_combat_creature*&	  best_target )
{
	t_simulated_combat_creature_list::iterator  index;
	int											result = 0;
	int											attacks = get_ai_spell_value( spell );
	t_simulated_combat_creature*				target;

	index = m_creatures.begin();
	best_target = 0;
	if (attacks > 1)
	{
		// choose secondary targets
		for (; index != m_creatures.end(); ++index)
		{
			target = *index;
			if (target->get_number() == 0)
				continue;
			if (target->belongs_to_defender() == caster.belongs_to_defender())
				continue;
			if (!can_affect_target( caster, spell, *target ))
				continue;

			result += get_spell_damage( caster, spell, *target );
			best_target = target;
			// if this is the last secondary target, increment index, but stop.
			if (--attacks == 1)
			{
				++index;
				break;
			}
		}
	}
	// choose primary target
	int best_value = 0;
	int value;

	for (; index != m_creatures.end(); ++index)
	{
		target = *index;
		if (target->get_number() == 0)
			continue;
		if (target->belongs_to_defender() == caster.belongs_to_defender())
			continue;
		if (!can_affect_target( caster, spell, *target ))
			continue;

		value = get_spell_damage( caster, spell, *target );
		if (value > best_value)
		{
			best_target = target;
			best_value = value;
		}
	}
	return result + best_value;
}


// ------------------------------------------------------------------------------
// choose a target to sacrifice, and return value of sacrifice
// ------------------------------------------------------------------------------
int t_simulated_combat::choose_sacrifice_target( t_simulated_combat_creature const& caster,
												 t_simulated_combat_creature*&		best_target,
												 t_simulated_combat_creature*&		best_client )
{
	t_simulated_combat_creature_list::iterator  index;
	t_simulated_combat_creature*				client;
	int											value;
	int											best_value = 0;
	int											result;

	best_target = 0;
	best_client = 0;
	// find the stack with the most casualties
	for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
	{
		client = *index;
		if (client->get_number() == 0)
			continue;
		if (client->belongs_to_defender() != caster.belongs_to_defender())
			continue;
		if (!can_affect_target( caster, k_spell_effect_sacrifice_client, *client ))
			continue;

		value = client->get_original_number() * client->get_hit_points()
		      - client->get_total_hits();
		if (best_client == 0 || value > best_value)
		{
			best_client = client;
			best_value = value;
		}
	}
	if (best_client == 0 || best_value == 0)
		return -1;
	result = best_value;

	t_simulated_combat_creature* target;
	int							 hits;

	// find how much each possible target is worth if resurrected
	for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
	{
		target = *index;
		if (target == best_client)
			continue;
		if (target->belongs_to_defender() != caster.belongs_to_defender())
			continue;
		if (target->is_summoned())
			continue;
		if (!can_affect_target( caster, k_spell_sacrifice, *target ))
			continue;
		hits = target->get_total_hits();
		if (hits >= result)
			continue;
		value = hits * 2;
		if (value > result)
			value = result;
		value -= hits;
		if (best_target == 0 || value > best_value)
		{
			best_target = target;
			best_value = value;
		}
	}
	if (best_target == 0 || best_value <= 0)
		return -1;
	return best_value;
}

// ------------------------------------------------------------------------------
// choose spell to cast
// ------------------------------------------------------------------------------
int t_simulated_combat::choose_spell( t_simulated_combat_creature const& attacker,
									  t_spell&							 best_spell,
									  t_simulated_combat_creature*&		 best_target,
									  t_simulated_combat_creature*&		 best_client )
{
	int							 result = -1;
	t_spell						 spell;
	int							 value;
	t_simulated_combat_creature* target = 0;
	t_simulated_combat_creature* client = 0;

	if (attacker.get_spell_points() <= 0)
		return -1;

	best_spell = k_spell_none;
	best_target = 0;
	best_client = 0;
	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (!attacker.can_cast( spell ))
			continue;

		value = choose_spell_target( attacker, spell, target, client );
		if (value <= 0)
			continue;

		if (best_spell != k_spell_none && value <= result)
			continue;

		best_spell = spell;
		best_target = target;
		best_client = client;
		result = value;
	}
	return result;
}

static int get_summoning_value( t_simulated_combat_creature const& caster,
							    t_spell							   spell ) 
{
	t_creature_type creature_type = get_summoned_creature( spell );
	int				power = caster.get_spell_power( spell ) * caster.get_spell_offense();

	if (creature_type == k_creature_none)
	{ // summoning spells without creatures have power expressed in hit points,
	  // not actual number of creatures
		creature_type = k_pikeman;
		power = power / get_traits( creature_type ).hit_points;
		if (power < 1)
			return 0;
	}
	return get_traits( creature_type ).hit_points * power;
}

// ------------------------------------------------------------------------------
// choose spell to cast
// ------------------------------------------------------------------------------
int t_simulated_combat::choose_spell_target( t_simulated_combat_creature const&	attacker,
											 t_spell							spell,
											 t_simulated_combat_creature*&		best_target )
{
	t_simulated_combat_creature_list::iterator  index;
	int											best_value = 0;
	int											value;
	t_simulated_combat_creature*				target;
	t_ai_spell_type								spell_type;

	best_target = 0;
	spell_type = get_ai_spell_type( spell );
	for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
	{
		target = *index;
		if (target->get_number() == 0)
			continue;
		if (!can_affect_target( attacker, spell, *target ))
			continue;
		// additional check because of the abstraction of some spells like displacement
		if (spell_type == k_ai_spell_type_blessing
			&& target->belongs_to_defender() != attacker.belongs_to_defender())
			continue;
		if ((spell_type == k_ai_spell_type_curse 
			 || spell_type == k_ai_spell_type_hand_of_death)
			&& target->belongs_to_defender() == attacker.belongs_to_defender())
			continue;
		value = get_spell_target_value( attacker, spell, *target );
		if (best_target == 0 || value > best_value)
		{
			best_target = target;
			best_value = value;
		}
	}
	return best_value;
}

// ------------------------------------------------------------------------------
// choose spell to cast
// ------------------------------------------------------------------------------
int t_simulated_combat::choose_spell_target( t_simulated_combat_creature const&	attacker,
											 t_spell							spell,
											 t_simulated_combat_creature*&		best_target,
											 t_simulated_combat_creature*&		best_client )
{
	t_ai_spell_type spell_type;

	spell_type = get_ai_spell_type( spell );
	best_target = 0;
	best_client = 0;
	if (is_mass_effect( spell ))
		return get_mass_spell_value( attacker, spell );

	switch (spell_type)
	{
		case k_ai_spell_type_none:
			return -1;

		case k_ai_spell_type_summoning:
			return get_summoning_value( attacker, spell );

		case k_ai_spell_type_sacrifice:
			return choose_sacrifice_target( attacker, best_target, best_client );

		case k_ai_spell_type_damage:
			return choose_damage_target( attacker, spell, best_target );

		default:
			return choose_spell_target( attacker, spell, best_target );
	}
	return -1;
}

// ------------------------------------------------------------------------------
// determine effect of spells which damage everyone
// ------------------------------------------------------------------------------
int t_simulated_combat::get_mass_spell_value( t_simulated_combat_creature const& caster,
										      t_spell							  spell ) const
{
	t_simulated_combat_creature_list::const_iterator	index;
	int													effect;
	int													hits;
	int													total_effect[2];
	int													total_hits[2];										
	t_simulated_combat_creature*						target;

	memset( total_effect, 0, sizeof( total_effect ));
	memset( total_hits, 0, sizeof( total_hits ));
	for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
	{
		target = *index;
		if (target->get_number() == 0)
			continue;
		if (!can_affect_target( caster, spell, *target ))
			continue;

		hits = target->get_total_hits();
		total_hits[target->belongs_to_defender()] += hits;
		effect = get_spell_target_value( caster, spell, *target );
		total_effect[ target->belongs_to_defender() ] += effect;
	}

	bool defender = caster.belongs_to_defender();

	if (get_ai_spell_type( spell ) == k_ai_spell_type_blessing)
	{
		// don't use if enemy receives larger benefit
		if (total_effect[defender] < total_effect[!defender])
			return -1;
		// don't use if enemy receives larger percentage benefit
		if (total_effect[defender] * total_hits[!defender]
			<= total_effect[!defender] * total_hits[defender])
			return -1;
		return total_effect[defender] - total_effect[!defender];
	}

	// do not cast if we do more damage to ourselves
	if (total_effect[ defender ] >= total_effect[ !defender ])
		return -1;

	// do not cast if we do proportionately more damage to ourselves
	if (total_effect[ defender ] * total_hits[!defender]
	    >= total_effect[ !defender ] * total_hits[ defender ])
		return -1;
	return total_effect[ !defender ] - total_effect[ defender ];
}

// ------------------------------------------------------------------------------
// adjust value of curse / blessing 
// ------------------------------------------------------------------------------
static int get_enchantment_value( t_simulated_combat_creature const&	caster,
							      t_spell								spell,
							      t_simulated_combat_creature const&	target,
							      int									base_value,
							      bool const*							has_magic )
{
	int result = 0;

	if (affects_offense( spell ))
	{
		if (!affects_magic( spell ) || target.casts_spells() )
			result = base_value;
	}
	if (affects_defense( spell ))
	{
		if (!affects_magic( spell ) || has_magic[!target.belongs_to_defender()])
			result += base_value;
	}
	// if it affects magic, make a guess at whether it matters.
	if (!affects_magic( spell ))
		result = result * caster.get_spell_offense();
	return result;
}

// ------------------------------------------------------------------------------
// determine value of a given spell
// ------------------------------------------------------------------------------
int t_simulated_combat::get_spell_target_value( t_simulated_combat_creature const& attacker,
											    t_spell							   spell,
												t_simulated_combat_creature const& target ) const
{
	t_ai_spell_type spell_type;
	int				result = 0;
	int				base_value;
	int				power;

	spell_type = get_ai_spell_type( spell );
	switch (spell_type)
	{
		case k_ai_spell_type_blessing:
			// basic value is percentage of hits / 2, because offense and defense
			// are each half of the picture.  Less accurate than square root,
			// but faster.
			base_value = target.get_total_hits() * get_ai_spell_value( spell ) / 200.0;
			return get_enchantment_value( attacker, spell, target, base_value, m_has_magic );

		case k_ai_spell_type_curse:
			// curses reduce the value of the target.  Treat the result
			// as the current value minus the new, reduced value.  Cut that in half,
			// since offense and defense are both half of the picture.
			power = get_ai_spell_value( spell );
			if (!affects_magic( spell ))
				power = power / target.get_spell_defense();
			base_value = (target.get_total_hits() 
				        - target.get_total_hits() * 100 / (100 + power)) / 2;
			return get_enchantment_value( attacker, spell, target, base_value, m_has_magic );

		case k_ai_spell_type_damage:
			return get_spell_damage( attacker, spell, target );

		case k_ai_spell_type_hand_of_death:
			return get_spell_damage( attacker, spell, target,
				                     attacker.get_spell_power( spell ) * target.get_hit_points() );

		case k_ai_spell_type_healing:
			base_value = attacker.get_spell_offense() * attacker.get_spell_power( spell );
			if (base_value > target.get_wounds())
				base_value = target.get_wounds();
			return base_value;

		case k_ai_spell_type_resurrection:
			result = attacker.get_spell_offense() * attacker.get_spell_power( spell );
			base_value = target.get_original_number() * target.get_hit_points()
				       - target.get_total_hits();
			if (base_value < result)
				result = base_value;
			return result;

		default:
			assert( false );
	}
	return 0;
}

// -----------------------------------------------------------
// initialize combat
// -----------------------------------------------------------
void t_simulated_combat::initialize()
{
	// determine magic and tactics skills for each side
	t_simulated_combat_creature_list::iterator  index;
	t_simulated_combat_creature*				creature;
	bool										defender;
	int											i;
	double										bonus;
	t_creature_stack*							stack;

	m_result = k_result_unknown;
	m_turns = 1;
	memset( m_has_magic, 0, sizeof( m_has_magic ));
	for (i = 0; i < 2; ++i)
	{
		m_tactics_offense_bonus[i] = 1.0;
		m_tactics_defense_bonus[i] = 1.0;
	}

	for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
	{
		creature = *index;
		stack = creature->get_creature_stack();
		defender = creature->belongs_to_defender();
		if (creature->casts_spells())
			m_has_magic[defender] = true;
		bonus = stack->get_tactics_offense_bonus( m_sea_battle ) / 100.0 + 1.0;
		if (bonus > m_tactics_offense_bonus[defender])
			m_tactics_offense_bonus[defender] = bonus;
		bonus = stack->get_tactics_defense_bonus( m_sea_battle ) / 100.0 + 1.0;
		if (bonus > m_tactics_defense_bonus[defender])
			m_tactics_defense_bonus[defender] = bonus;
	}
	// add bonuses to defender for castle walls
	static double const k_bonus[] = { 1.0, 1.25, 1.5, 2.0 };

	t_town_image_level level = m_armies[true]->get_castle_level();

	m_tactics_defense_bonus[true] *= k_bonus[level];
	m_tactics_offense_bonus[true] *= k_bonus[level];

	// set offense, defense based on tactics bonus
	for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
	{
		creature = *index;
		if (creature->get_creature_type() == k_creature_none)
			continue;
		defender = creature->belongs_to_defender();
		creature->set_offense( m_tactics_offense_bonus[defender] );
		creature->set_defense( m_tactics_defense_bonus[defender] );
	}
}

// -----------------------------------------------------------
// class which depicts a simulate combat between two sides
// run the combat, but do not update armies
// -----------------------------------------------------------
void t_simulated_combat::run()
{
	bool										last_was_defender = true;
	t_simulated_combat_creature*				acting_creature;
	t_simulated_combat_creature_list::iterator  index;

	initialize();
	do
	{
		acting_creature = select_next_creature( last_was_defender );
		if (acting_creature == 0)
		{

			for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
			{
				index->get()->process_new_turn();
			}
			acting_creature = select_next_creature( last_was_defender );
			if (acting_creature == 0)
				break;
			m_turns++;
		}
		last_was_defender = acting_creature->belongs_to_defender();
	} while (choose_action( *acting_creature ));

	// figure out which side won
	bool							attackers_alive = false;
	bool							defenders_alive = false;
	t_simulated_combat_creature*	creature;

	// check if one side is dead.
	for (index = m_creatures.begin(); index != m_creatures.end(); index++)
	{
		creature = *index;
		if (creature->get_number() <= 0)
			continue;
		if (creature->get_first_casualty_time() < 0)
			creature->set_first_casualty_time( 100 );
		creature->set_lifespan( m_turns + 1 );
		if (creature->is_summoned())
			continue;
		if (creature->belongs_to_defender())
			defenders_alive = true;
		else
			attackers_alive = true;
	}

	if (!attackers_alive && !defenders_alive)
		m_result = k_result_both_lost;
	else if (!attackers_alive)
		m_result = k_result_attacker_lost;
	else
		m_result = k_result_defender_lost;
}


// -----------------------------------------------------------
// class which depicts a simulate combat between two sides
// report the results to the human participant.
// FOR NOW, at least, it must run AFTER record_results() if
// it is called at all.
// -----------------------------------------------------------
void t_simulated_combat::report_results( t_adventure_frame* window )
{
	report_combat_results( m_armies, m_players, m_losses, m_result, 
						   window, m_town, m_location, m_leaders );
}


// -----------------------------------------------------------
// class which depicts a simulate combat between two sides
// update the armies
// FOR NOW, at least, it must run BEFORE report_results()
// -----------------------------------------------------------
void t_simulated_combat::record_results( t_adventure_frame* window )
{
	t_simulated_combat_creature*				creature;
	int											role;
	t_simulated_combat_creature_list::iterator  index;

	// if both players are computers, resurrect the winner's heroes.
	if ((m_players[k_side_attacker] == 0 || m_players[k_side_attacker]->is_computer())
		&& (m_players[k_side_defender] == 0 || m_players[k_side_defender]->is_computer())
		&& m_result != k_result_both_lost)
	{
		bool defender_winner = defender_won( m_result );
		
		for (index = m_creatures.begin(); index != m_creatures.end(); index++)
		{
			creature = *index;
			if (creature->get_hero() == 0)
				continue;
			creature->resurrect( 1 );
		}
	}

	// count casualties.
	for (index = m_creatures.begin(); index != m_creatures.end(); index++)
	{
		creature = *index;
		creature->redistribute_artifacts( m_armies, m_booty );
		creature->record_losses( m_losses );
		creature->update_original_army( m_armies );
	}

	// consolidate neutrals
	for (role = 0; role <= 1; role++)
	{
		if (m_players[role] == 0 || m_players[role]->is_computer())
			m_armies[role]->consolidate();
	}

	t_town *             retreat_towns[2];
	t_level_map_point_2d retreat_locations[2];

	// This will force post_process_combat_results to figure it out if it needs them
	retreat_towns[0] = NULL;
	retreat_towns[1] = NULL;

	// Post process the results to deal with relocation, damage, cost, etc.
	post_process_combat_results( m_armies, m_players, m_losses, m_result, m_location, 
								 window, m_town );
}

void t_simulated_combat::finalize_combat( t_adventure_frame* window, 
										  bool const * are_real_armies,
										  t_army_ptr & new_army_ptr )
{
	int i, j;

	for (i = 0; i < 2; ++i)
		for (j = 0; j < t_creature_array::k_size; ++j)
			(*m_armies[i])[j].clear_temporary_bonuses();

	// Post process the results to deal with relocation, damage, cost, etc.
	post_process_apply_combat_surrender( m_armies, m_players, m_losses, m_result, m_location, 
										window, m_town, m_booty, are_real_armies, new_army_ptr );
}

// -----------------------------------------------------------
// class which depicts a simulate combat between two sides
// -----------------------------------------------------------
t_simulated_combat_creature* t_simulated_combat::select_next_creature( bool last_was_defender )
{
	t_simulated_combat_creature_list::iterator  index;
	int											speed;
	int											fastest;
	t_simulated_combat_creature*				creature;
	t_simulated_combat_creature*				best_creature = 0;

	// simplest possible decision making: choose the closest creature,
	// and between equally close creatures, choose the one that has not retaliated.
	for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		if (creature->has_moved())
			continue;
		speed = creature->get_speed();
		if (best_creature != 0)
		{
			if (speed < fastest)
				continue;
			if (speed == fastest)
			{
				if (best_creature->belongs_to_defender() == last_was_defender)
					continue;
			}
		}
		fastest = speed;
		best_creature = creature;
	}
	return best_creature;
}

// -----------------------------------------------------------
// class which depicts a simulate combat between two sides
// -----------------------------------------------------------
void t_simulated_combat::set_effectiveness( bool defender, double effectiveness )
{
	t_simulated_combat_creature_list::iterator	index;
	t_simulated_combat_creature*				creature;

	for (index = m_creatures.begin(); index != m_creatures.end(); ++index)
	{
		creature = *index;
		if (creature->belongs_to_defender() != defender)
			continue;

		creature->set_effectiveness( effectiveness );
	}
}

// -----------------------------------------------------------
// class which depicts a simulate combat between two sides
// -----------------------------------------------------------
void t_simulated_combat::update_attacking_force()
{
	t_simulated_combat_creature*				creature;
	t_simulated_combat_creature_list::iterator  index;
	
	// count casualties.
	for (index = m_creatures.begin(); index != m_creatures.end(); index++)
	{
		creature = *index;
		if (!creature->belongs_to_defender())
			creature->update_original_army( m_armies );
	}

	for (int attacker = 0; attacker < 2; attacker++)
	{
		// consolidate neutrals
		if (m_players[ attacker ] == 0)
			m_armies[  attacker ]->consolidate();
	}
}

// -----------------------------------------------------------
// class which depicts a simulate combat between two sides
// -----------------------------------------------------------
float t_simulated_combat::query_combat_value() const
{
	t_adventure_ai const& ai = m_armies[0]->get_map()->get_ai();
	
	t_ai_army			attacker( m_armies[0]->get_map(), m_armies[0]->get_owner() );
	t_ai_army			defender( m_armies[1]->get_map(), m_armies[1]->get_owner() );
	int					attacker_index = 0,
						defender_index = 0;
	t_creature_array	losses[2];
	
	t_simulated_combat_creature_list::const_iterator  index;

	// Create resultant armies using the simulated creatures (and build the losses arrays)
	for (index = m_creatures.begin(); index != m_creatures.end(); index++)
	{
		t_simulated_combat_creature* creature = *index;

		creature->record_losses(losses);
		
		if ((creature->get_number() == 0) || creature->is_summoned() )
			continue;
		
		if (creature->belongs_to_defender())
		{
			if (defender_index > 6)
			{
				assert(false);
				continue;
			}
			if (creature->get_hero() != NULL)
				defender.add( creature->get_creature_stack(), defender_index++ );
			else
				defender.add( creature->get_creature_type(), creature->get_number(), defender_index++ );
		}
		else
		{
			if (attacker_index > 6)
			{
				assert(false);
				continue;
			}
			
			if (creature->get_hero() != NULL)
				attacker.add( creature->get_creature_stack(), attacker_index++ );
			else
				attacker.add( creature->get_creature_type(), creature->get_number(), attacker_index++ );
		}
	}

	// Initial result is the delta of our before and after values.	
	float	aggression_multiplier = ai.scale_for_combat_aggression( m_armies[0]->get_owner(), m_armies[1]->get_owner() );
	float	defender_before = m_armies[1]->ai_value();
	float	defender_after = defender.ai_value();
	float	result = 0.0f;
	
	if (m_armies[1]->get_owner() == NULL)
		result = attacker.ai_value() - m_armies[0]->ai_value();

	if ( attacker_won(get_result()) )  
	{
		result += m_armies[1]->ai_value() * aggression_multiplier;

		result += ai_value_of_xp_for_army( m_armies[0], m_armies[1]->get_experience_value() );

		t_creature_array necroed_creatures, raised_creatures;
		t_hero* necromancer = NULL;
		do_necromancy(	*m_armies[0], m_armies[0]->get_owner(), 
						losses[1], &necromancer, necroed_creatures );

		// Just the value of the creatures...  I could potential record the value of adding them to the army.
		result += necroed_creatures.ai_value();

		do_resurrection( *m_armies[0], losses[0], raised_creatures, false );

		result += raised_creatures.ai_value();
	}
	else 
	{
		result = k_value_army_death;
	}

	return result;
}

float t_simulated_combat::query_value_drop() const
{
	t_ai_army			attacker(m_armies[0]->get_map(), m_armies[0]->get_owner());
	int					attacker_index = 0;
	
	t_simulated_combat_creature_list::const_iterator  index;

	for (index = m_creatures.begin(); index != m_creatures.end(); index++)
	{
		t_simulated_combat_creature* creature = *index;

		if ( creature->belongs_to( true ) || (creature->get_number() == 0) || creature->is_summoned() )
			continue;

		if (attacker_index > 6)
			break;

		if (creature->get_hero() != NULL)
			attacker.add( creature->get_creature_stack(), attacker_index++ );
		else
			attacker.add( creature->get_creature_type(), creature->get_number(), attacker_index++ );
	}

	float value_before = m_armies[0]->ai_value();
	if (value_before == 0.0f)
		return 0.0f;

	return 1.0f - (attacker.ai_value() / value_before);
}

t_creature_array const* t_simulated_combat::get_losses() const 
{
	return m_losses;
}
